/*
 * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
 * http://www.gnu.org/licenses/gpl-3.0.html
 *
 * $Revision: 9376 $
 * $Id: cbprofiler.cpp 9376 2013-10-01 20:30:42Z fuscated $
 * $HeadURL: http://svn.code.sf.net/p/codeblocks/code/branches/release-17.xx/src/plugins/contrib/profiler/cbprofiler.cpp $
 */

#include "sdk.h"
#ifndef CB_PRECOMP
    #include <wx/datetime.h>
    #include <wx/filename.h>
    #include <wx/fs_zip.h>
    #include <wx/intl.h>
    #include <wx/string.h>
    #include <wx/xrc/xmlres.h>
    #include "cbproject.h"
    #include "configmanager.h"
    #include "globals.h"
    #include "macrosmanager.h"
    #include "manager.h"
    #include "logmanager.h"
    #include "projectbuildtarget.h"
    #include "projectmanager.h"
#endif
#include <wx/choicdlg.h>
#include <wx/filedlg.h>
#include "cbprofiler.h"
#include "cbprofilerconfig.h"
#include "cbprofilerexec.h"
#include "prep.h"

// Register the plugin
namespace
{
    PluginRegistrant<CBProfiler> reg(_T("Profiler"));
};

CBProfiler::CBProfiler()
{
    // 构造函数
    if(!Manager::LoadResource(_T("Profiler.zip")))
    {
        NotifyMissingFile(_T("Profiler.zip"));
    }
}

CBProfiler::~CBProfiler()
{
    // 析构函数
}

void CBProfiler::OnAttach()
{
    // do whatever initialization you need for your plugin
    // NOTE: after this function, the inherited member variable
    // IsAttached() will be TRUE...
    // You should check for it in other functions, because if it
    // is FALSE, it means that the application did *not* "load"
    // (see: does not need) this plugin...
    //
    // 为你的插件做任何你需要的初始化
    // 注:在此函数之后,继承的成员变量IsAttached()将为TRUE...
    // 您应该在其他函数中检查它,因为如果它为FALSE,则意味着应用程序没有"加载"(请参阅:不需要)此插件...
    //
}

void CBProfiler::OnRelease(bool /*appShutDown*/)
{
    // do de-initialization for your plugin
    // if appShutDown is false, the plugin is unloaded because Code::Blocks is being shut down,
    // which means you must not use any of the SDK Managers
    // NOTE: after this function, the inherited member variable
    // IsAttached() will be FALSE...
    //
    // 如果appShutDown为false,插件将被卸载,因为Code::Blocks正在关闭,这意味着您不能使用任何SDK管理器.
    // 注:在此函数之后,继承的成员变量IsAttached()将为FALSE...
    //
}

cbConfigurationPanel* CBProfiler::GetConfigurationPanel(wxWindow* parent)
{
    // if not attached, exit
    if (!IsAttached())
        return 0;

    CBProfilerConfigDlg* dlg = new CBProfilerConfigDlg(parent);
    return dlg;
}

int CBProfiler::Execute()
{
    // if not attached, exit
    if (!IsAttached())
        return -1;

    cbProject* project = Manager::Get()->GetProjectManager()->GetActiveProject();
    // if no project open, exit
    if (!project)
    {
        wxString msg = wxT("在使用插件之前,您需要打开一个项目!\nC::B代码分析器无法完成操作.");
        cbMessageBox(msg,wxT("错误"), wxICON_ERROR | wxOK, Manager::Get()->GetAppWindow());
        Manager::Get()->GetLogManager()->DebugLog(msg);
        return -1;
    }

    ProjectBuildTarget* target = 0L;
    if (project->GetBuildTargetsCount() > 1)
    {
        //多个可执行目标?询问...
        std::vector<wxString> choices(project->GetBuildTargetsCount());
        wxString active_target = project->GetActiveBuildTarget();
        int selected = 0;
        for (int i=0; i<project->GetBuildTargetsCount(); ++i)
        {
            choices[i] = project->GetBuildTarget(i)->GetTitle();
            if (choices[i] == active_target)
                selected = i;
        }
        wxSingleChoiceDialog dialog(Manager::Get()->GetAppWindow(),
                                    wxT("选择要分析的目标"),
                                    wxT("选择目标"),
                                    project->GetBuildTargetsCount(), &choices[0]);
        dialog.SetSelection(selected);
        if (dialog.ShowModal() != wxID_OK)
            return -1;
        int targetIndex = dialog.GetSelection();
        target = project->GetBuildTarget(targetIndex);
    }
    else if (project->GetBuildTargetsCount() == 1)
        target = project->GetBuildTarget(0);
    else
    {
        // not even one executable target...
        wxString msg = wxT("在项目中找不到可执行目标!\nC::B代码分析器无法完成操作.");
        cbMessageBox(msg,wxT("错误"), wxICON_ERROR | wxOK, Manager::Get()->GetAppWindow());
        Manager::Get()->GetLogManager()->DebugLog(msg);
        return -1;
    }

    if ((target->GetTargetType() != ttExecutable) && (target->GetTargetType() != ttConsoleOnly))
    {
        wxString msg = wxT("目标不可执行!");
        cbMessageBox(msg,wxT("错误"), wxICON_ERROR | wxOK, Manager::Get()->GetAppWindow());
        Manager::Get()->GetLogManager()->DebugLog(msg);
        return -1;
    }

    // Scope...
    wxString exename,dataname;

    if (target)
    {
        exename = target->GetOutputFilename();
        Manager::Get()->GetMacrosManager()->ReplaceEnvVars(exename);
        wxFileName ename(exename);
        ename.Normalize(wxPATH_NORM_ALL, project->GetBasePath());
        exename = ename.GetFullPath();

        wxChar separator = wxFileName::GetPathSeparator();

        // The user either hasn't built the target yet or cleaned the project
        if (!ename.FileExists())
        {
            wxString msg = wxT("找不到可执行文件!\n你要么没有建立目标,要么只是清理了项目.\n还是尝试查找配置信息?");
            if (cbMessageBox(msg,wxT("确认"),wxYES_NO | wxICON_QUESTION,
                             Manager::Get()->GetAppWindow()) == wxID_NO)
                return -2;
        }

        // We locate gmon.out. First: look for gmon.out in the folder of the executable
        // 我们找到了gmon.out.首先:在可执行文件的文件夹中查找gmon.out
        dataname=exename.BeforeLast(separator);
        dataname+=separator;
        dataname+=_T("gmon.out");
        wxFileName dname(dataname);

        // The gmon.out file doesn't exist in the executable's directory.
        // 可执行文件的目录中不存在gmon.out文件.
        if (!dname.FileExists())
        {
            // Second: look for gmon.out in the working directory of the project
            // 第二:在项目工作目录中查找gmon.out
            wxString workname = target->GetWorkingDir();
            Manager::Get()->GetMacrosManager()->ReplaceEnvVars(workname);
            wxFileName wname(workname);
            wname.Normalize(wxPATH_NORM_ALL, project->GetBasePath());
            workname = wname.GetFullPath();

            dataname=workname.BeforeLast(separator);
            dataname+=separator;
            dataname+=_T("gmon.out");
            dname = wxFileName(dataname);

            // The gmon.out file doesn't exist in the working directory.
            // 工作目录中不存在gmon.out文件.
            if (!dname.FileExists())
            {
                wxString msg = wxT("找不到配置文件数据!\n确保为当前目标启用'执行时配置文件代码'.\n在此之后,目标必须至少运行过一次.\n是否要搜索配置文件数据文件?");
                if (cbMessageBox(msg, wxT("找不到gmon.out."), wxICON_QUESTION | wxYES_NO, Manager::Get()->GetAppWindow()) == wxID_NO)
                    return -1;
                else
                {
                    wxFileDialog filedialog(Manager::Get()->GetAppWindow(), wxT("定位配置文件信息"),
                                            _T(""),_T("gmon.out"),_T("*.*"),wxFD_OPEN|wxFD_FILE_MUST_EXIST|compatibility::wxHideReadonly);
                    if (filedialog.ShowModal() == wxID_OK)
                    {
                        dataname = filedialog.GetPath();
                        dname = wxFileName(dataname);
                    }
                    else return -1;
                }
            }
        }

        Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("正在使用来自%s的可执行文件"),exename.wx_str()));
        Manager::Get()->GetLogManager()->DebugLog(wxString::Format(wxT("正在使用来自以下位置的gmon.out文件:%s") , dataname.wx_str()));

        // If we got so far, it means both the executable and the profile data exist
        // 如果到目前为止,这意味着可执行文件和配置文件数据都存在
        wxDateTime exetime  = ename.GetModificationTime();
        wxDateTime datatime = dname.GetModificationTime();

        // Profile data might be old...
        // 配置文件数据可能很旧
        if (exetime>datatime)
        {
            wxString msg = wxT("配置文件数据似乎比可执行文件要旧.\n您可能没有运行可执行文件来更新此数据.\n还是继续?");
            if (cbMessageBox(msg,wxT("确认"),wxYES_NO | wxICON_QUESTION,
                             Manager::Get()->GetAppWindow()) == wxID_NO)
                return -2;
        }
    }

    ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("cbprofiler"));

    // Loading configuration
    // 载入配置
    struct_config config;
    config.chkAnnSource        = cfg->ReadBool(_T("/ann_source_chk"), false);
    config.txtAnnSource        = cfg->Read(_T("/ann_source_txt"), wxEmptyString);
    config.chkMinCount         = cfg->ReadBool(_T("/min_count_chk"), false);
    config.spnMinCount         = cfg->ReadInt(_T("/min_count_spn"), 0);
    config.chkBrief            = cfg->ReadBool(_T("/brief"), false);
    config.chkFileInfo         = cfg->ReadBool(_T("/file_info"), false);
    config.chkUnusedFunctions  = cfg->ReadBool(_T("/unused_functions"), false);
    config.chkStaticCallGraph  = cfg->ReadBool(_T("/static_call_graph"), false);
    config.chkNoStatic         = cfg->ReadBool(_T("/no_static"), false);
    config.chkMinCount         = cfg->ReadBool(_T("/min_count_chk"), false);
    config.chkSum              = cfg->ReadBool(_T("/sum"), false);
    config.txtExtra            = cfg->Read(_T("/extra_txt"), wxEmptyString);

    // If we got this far, all is left is to call gprof!!!
    // 如果我们走到这一步,剩下的就是打电话给gprof!!!
    dlg = new CBProfilerExecDlg(Manager::Get()->GetAppWindow());

    // Do we need to show the dialog (process succesful)?
    // 是否需要显示对话框(处理成功)?
    if (dlg->Execute(exename, dataname, config) != 0)
        return -1;

    return 0;
}
